package com.dz.lm.common.util;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import javax.net.ssl.SSLContext;

import com.alibaba.fastjson.JSON;
import net.sf.json.JSONObject;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import com.dz.lm.common.utils.StringUtils;
import com.dz.lm.common.utils.XmlResolve;
import com.dz.lm.common.utils.random.RandomUtils;

/**
 * 微信支付
 */
@Component
public class WxPay {
	private static int socketTimeout = 10000;// 连接超时时间，默认10秒
    private static int connectTimeout = 30000;// 传输超时时间，默认30秒
	private static final Logger logger = LoggerFactory.getLogger(WxPay.class);
	private static InputStream in;
	private static Properties p;
	static {
		try {
			in = WxPay.class.getClassLoader().getResourceAsStream("wx_pay_info.properties");
			p = new Properties();
			p.load(in);
		} catch (FileNotFoundException e) {
			logger.error("WxPay Not Found File :wx_pay_info.properties");
			e.printStackTrace();
		} catch (IOException e) {
			logger.error("WxPay IOException");
			e.printStackTrace();
		}
	}

	/**
	 * 获取微信支付token
	 * @param inParam
	 * @return
	 * @throws Exception
	 */
	public Map<String, Object> unifiedOrder(Map<String, Object> inParam) throws Exception {
		Map<String, Object> retData = null;
		// 发送内容
		//微信支付商户APPID
		String appid = p.getProperty("appid");
		//商户号
		String mch_id = p.getProperty("mch_id");
		//商户平台设置的密钥
		String key = p.getProperty("key");
		//签名类型
		String sign_type = p.getProperty("sign_type");
		//交易类型
		String trade_type = p.getProperty("trade_type");
		//回调URL
		String notify_url = p.getProperty("notify_url");
		//统一下单接口链接
		String unifiedorder_url = p.getProperty("unifiedorder_url");
    	String nonce_str = RandomUtils.generateString(32).toUpperCase();
    	//参数组合
    	Map<String, Object> param = new HashMap<String, Object>();

    	param.put("appid", appid);
		param.put("trade_type", trade_type);
		param.put("mch_id", mch_id);
    	param.put("device_info", inParam.get("device_info"));
    	param.put("nonce_str", nonce_str);
    	param.put("sign_type", sign_type);
    	param.put("body", inParam.get("body"));
    	param.put("attach",inParam.get("attach"));
    	param.put("out_trade_no", inParam.get("out_trade_no"));
    	String total_fee = String.valueOf(inParam.get("total_fee"));
    	param.put("total_fee", Integer.parseInt(total_fee.substring(0, total_fee.indexOf("."))));
    	param.put("spbill_create_ip", inParam.get("spbill_create_ip"));
    	param.put("notify_url", notify_url + "/"  + inParam.get("out_trade_no") +"/wxPayCallBack.do");
		//小程序支付
		if(StringUtil.getString(inParam.get("platform")) .equals("wexin")){
			String code = StringUtil.getString(inParam.get("wxcode"));
			param.put("trade_type", p.getProperty("wxtrade_type"));
			param.put("appid", p.getProperty("wxappid"));
			String openId = this.getOpenId(code, p.getProperty("wxappid"), p.getProperty("appSecret"));
			if(StringUtil.containEmpty(openId)){
				return retData;
			}else {
				param.put("openid",openId);
			}
		}
    	String signature = getSignOrderStr(param) + "&key=" + key;
    	signature = MD5(signature);
    	param.put("sign",signature);
    	String xmlParam = XmlResolve.callMapToXML(param);
		PostMethod postMethod = new PostMethod(unifiedorder_url);
		try {
			postMethod.getParams().setContentCharset("UTF-8");
			postMethod.addRequestHeader("Connection", "close");
			postMethod.addRequestHeader("Content-Type", "application/x-www-form-urlencoded;");
			postMethod.setRequestBody(xmlParam);
			HttpClient httpClient = new HttpClient();
			int statusCode = httpClient.executeMethod(postMethod);
			String response = postMethod.getResponseBodyAsString();
			logger.info("getToken statusCode: " + statusCode + ", body: "+ response);
			
			if(200 == statusCode && !StringUtils.isEmpty(response)){
				retData = XmlResolve.callXMLToMap(response);
				if (!"SUCCESS".equals(retData.get("return_code"))) {
					logger.info("微信支付返回错误"+response);
				}
			}else{
				throw new Exception("微信支付返回结果解析错误");
			}
		} catch (Exception e) {
			logger.info("微信支付返回错误" + e.getStackTrace());
			throw new Exception("微信支付返回错误", e);
		} finally {
			// 释放连接
			postMethod.releaseConnection();
		}
        return retData;
	}
	
	/**
	 * 查询订单
	 * @param inParam
	 * @return
	 * @throws Exception
	 */
	public Map<String, Object> orderquery(Map<String, Object> inParam) throws Exception {
		Map<String, Object> retData = null;
		// 发送内容
		//微信支付商户APPID
		String appid = p.getProperty("appid");
		//商户号
		String mch_id = p.getProperty("mch_id");
		//商户平台设置的密钥
		String key = p.getProperty("key");
		//统一下单接口链接
		String orderquery_url = p.getProperty("orderquery_url");
    	String nonce_str = RandomUtils.generateString(32).toUpperCase();
    	//参数组合
    	Map<String, Object> param = new HashMap<String, Object>();
    	param.put("appid", appid);
    	param.put("mch_id", mch_id);
    	param.put("transaction_id", inParam.get("transactionId"));
    	param.put("nonce_str", nonce_str);
    	
    	String signature = getSignOrderStr(param) + "&key=" + key;
    	signature = MD5(signature);
    	param.put("sign",signature);
    	String xmlParam = XmlResolve.callMapToXML(param);
		PostMethod postMethod = new PostMethod(orderquery_url);
		try {
			postMethod.getParams().setContentCharset("UTF-8");
			postMethod.addRequestHeader("Connection", "close");
			postMethod.addRequestHeader("Content-Type", "application/x-www-form-urlencoded;");
			postMethod.setRequestBody(xmlParam);
			HttpClient httpClient = new HttpClient();
			int statusCode = httpClient.executeMethod(postMethod);
			String response = postMethod.getResponseBodyAsString();
			logger.info("getToken statusCode: " + statusCode + ", body: "+ response);
			
			if(200 == statusCode && !StringUtils.isEmpty(response)){
				retData = XmlResolve.callXMLToMap(response);
				if (!"SUCCESS".equals(retData.get("return_code"))) {
					logger.info("微信查询订单返回错误"+response);
				}
			}else{
				throw new Exception("微信支付返回结果解析错误");
			}
		} catch (Exception e) {
			logger.info("微信查询订单返回错误" + e.getStackTrace());
			throw new Exception("微信查询订单返回错误", e);
		} finally {
			// 释放连接
			postMethod.releaseConnection();
		}
        return retData;
	}
	
	/**
	 * 用户提现（企业付款到到零钱）
	 * 
	 * @param inParam
	 * @return
	 * @throws Exception
	 */
	public Map<String, Object> withdraw(Map<String, Object> inParam) throws Exception {
		Map<String, Object> retData = null;
		// 发送内容
		//微信支付商户APPID
		String appid = p.getProperty("appid");
		//商户号
		String mch_id = p.getProperty("mch_id");
		//商户平台设置的密钥
		String key = p.getProperty("key");
		//统一下单接口链接
		String mmpaymkttransfers_url = p.getProperty("mmpaymkttransfers_url");
    	String nonce_str = RandomUtils.generateString(32).toUpperCase();
    	String certPath = "apiclient_cert.p12";//p.getProperty("cert_path");
    	//参数组合
    	Map<String, Object> param = new HashMap<String, Object>();
    	param.put("mch_appid", appid);
    	param.put("mchid", mch_id);
    	param.put("device_info", inParam.get("device_info"));
    	param.put("nonce_str", nonce_str);
    	param.put("openid", inParam.get("openid") != null ? inParam.get("openid") : "");
    	param.put("partner_trade_no", inParam.get("partner_trade_no"));
    	param.put("check_name", "NO_CHECK");
    	param.put("amount", inParam.get("amount"));
    	param.put("desc", inParam.get("desc"));
    	param.put("spbill_create_ip", inParam.get("spbill_create_ip"));
    	if(inParam.get("wxcode") != null){ //小程序提现
			String wxcode = StringUtil.getString(inParam.get("wxcode"));
			String openId = this.getOpenId(wxcode, p.getProperty("wxappid"), p.getProperty("appSecret"));
			param.put("openid", openId);
			param.put("mch_appid", p.getProperty("wxappid"));
		}
    	String signature = getSignOrderStr(param) + "&key=" + key;
    	signature = MD5(signature);
    	param.put("sign",signature);
    	String xmlParam = XmlResolve.callMapToXML(param);
    	String result = "";
//		PostMethod postMethod = new PostMethod(mmpaymkttransfers_url);
		try {
//			postMethod.getParams().setContentCharset("UTF-8");
//			postMethod.getParams().setSoTimeout(socketTimeout);
//			postMethod.addRequestHeader("Connection", "close");
//			postMethod.addRequestHeader("Content-Type", "application/x-www-form-urlencoded;");
//			postMethod.setRequestBody(xmlParam);
			 HttpPost httpPost = new HttpPost(mmpaymkttransfers_url);
	        // 得指明使用UTF-8编码，否则到API服务器XML的中文不能被成功识别
	        StringEntity postEntity = new StringEntity(xmlParam, "UTF-8");
	        httpPost.addHeader("Content-Type", "text/xml");
	        httpPost.setEntity(postEntity);
	        // 根据默认超时限制初始化requestConfig
	        RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(socketTimeout).setConnectTimeout(connectTimeout).build();
	        // 设置请求器的配置
	        httpPost.setConfig(requestConfig);
	        
			CloseableHttpClient httpClient = initCert(mch_id, certPath);
	        try {
	            HttpResponse response = null;
	            try {
	                response = httpClient.execute(httpPost);
	            } catch (IOException e) {
	                e.printStackTrace();
	            }
	            HttpEntity entity = response.getEntity();
	            try {
	                result = EntityUtils.toString(entity, "UTF-8");
	                retData = XmlResolve.callXMLToMap(result);
					if (!"SUCCESS".equals(retData.get("return_code"))) {
						logger.info("微信企业付款到到零钱返回错误"+response);
					}
	            } catch (IOException e) {
	                e.printStackTrace();
	            }
	        } finally {
	            httpPost.abort();
	        }
	        

//			int statusCode = httpClient.execute(postMethod);
//			String response = postMethod.getResponseBodyAsString();
//			logger.info("getToken statusCode: " + statusCode + ", body: "+ response);
			
//			if(200 == statusCode && !StringUtils.isEmpty(response)){
//				retData = XmlResolve.callXMLToMap(response);
//				if (!"SUCCESS".equals(retData.get("return_code"))) {
//					logger.info("微信企业付款到到零钱返回错误"+response);
//				}
//			}else{
//				throw new Exception("微信企业付款到到零钱返回结果解析错误");
//			}
		} catch (Exception e) {
			logger.info("微信企业付款到到零钱返回错误" + e.getStackTrace());
			throw new Exception("微信企业付款到到零钱返回错误", e);
		} finally {
			// 释放连接
//			postMethod.releaseConnection();
		}
        return retData;
	}

	 /**
     * 加载证书
     * 
     * @param mchId 商户ID
     * @param certPath 证书位置
     * @throws Exception
     */
    private static CloseableHttpClient initCert(String mchId, String certPath) throws Exception {
        // 证书密码，默认为商户ID
        String key = mchId;
        // 证书的路径
        String path = certPath;
        // 指定读取证书格式为PKCS12
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        // 读取本机存放的PKCS12证书文件
        InputStream instream = WxPay.class.getClassLoader().getResourceAsStream("apiclient_cert.p12");
        try {
            // 指定PKCS12的密码(商户ID)
            keyStore.load(instream, key.toCharArray());
        } finally {
            instream.close();
        }
        SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, key.toCharArray()).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
        		sslcontext, 
        		new String[] {"TLSv1"}, 
        		null, 
        		SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(sslsf)
                .build();
        return httpClient;
    }
    
    /**
     * 生成签名字符串
     * @param params
     * @return
     */
    public String getSign(Map<String, Object> params) {
		//商户平台设置的密钥
		String key = p.getProperty("key");
    	String signature = getSignOrderStr(params) + "&key=" + key;
    	try {
			signature = MD5(signature);
		} catch (Exception e) {
			e.printStackTrace();
		}
    	return signature;
    }
	/**
	 * 小程序生成签名字符串
	 * @param params
	 * @return
	 */
	public String getwxSign(Map<String, Object> params) {
		//商户平台设置的密钥
		String key = p.getProperty("key");
		String signature = "appId="+params.get("appid") + "&nonceStr=" + params.get("nonce_str")+"&package=prepay_id="+params.get("prepay_id")+"&signType=MD5&timeStamp="+params.get("timeStamp")+"&key="+key;
		logger.info("signature================================"+signature);
		try {
			signature = MD5(signature);
		} catch (Exception e) {
			e.printStackTrace();
		}
		params.put("paySign",signature);
		return signature;
	}

    /**
     * 获取拼装好的待签名串
     * @param params
     * @return
     */
    public String getSignOrderStr(Map<String, Object> params) {
        StringBuilder sb = new StringBuilder();
		List<String> list = new ArrayList<String>();
		Set<String> keySet = params.keySet();
		Iterator<String> it = keySet.iterator();
		while (it.hasNext()) {
			String key = it.next();
			if (key != null && !"sign".equals(key)) {
				list.add(key);
			}
		}
		Collections.sort(list);
		for (int i = 0; i < list.size(); i++) {
			if (params.get(list.get(i)) != null && !"".equals(params.get(list.get(i)))) {
				sb.append(list.get(i));
				sb.append("=");
				sb.append(params.get(list.get(i)));
				sb.append("&");
			}
		}
        return sb.toString().substring(0, sb.toString().length() - 1);
    }
    
    /**
     * 生成 MD5
     *
     * @param data 待处理数据
     * @return MD5结果
     */
    public String MD5(String data) throws Exception {
        java.security.MessageDigest md = MessageDigest.getInstance("MD5");
        byte[] array = md.digest(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte item : array) {
            sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
        }
        return sb.toString().toUpperCase();
    }
    
    /**
     * 生成调起支付接口参数
     *
     * @param inParam 待处理数据
     * @return MD5结果
     */
    public Map<String, Object> getOrderParam(Map<String, Object> inParam) throws Exception {
		//商户平台设置的密钥
		String key = p.getProperty("key");
    	Map<String, Object> param = new HashMap<String, Object>();
    	Date day = new Date();
    	Timestamp ts = new Timestamp(day.getTime());
    	param.put("appid", inParam.get("appid"));
    	param.put("partnerid", inParam.get("mch_id"));
    	param.put("prepayid", inParam.get("prepay_id"));
    	param.put("package", "Sign=WXPay");
    	param.put("noncestr", inParam.get("nonce_str"));
    	param.put("timestamp", ts.getTime());
    	String signature = getSignOrderStr(param) + "&key=" + key;
    	signature = MD5(signature);
    	param.put("sign",signature);
    	return param;
    }
    public String getOpenId(String code,String wxAppid,String appSecret){
    	String openId = "";
    	Map map = new HashMap();
    	map.put("appid",wxAppid);
		map.put("secret",appSecret);
		map.put("js_code",code);
		map.put("grant_type","authorization_code");
		String result = HttpClientUtil.doGet("https://api.weixin.qq.com/sns/jscode2session", map);
		if(!StringUtil.containEmpty(result)){
			Map map1 = JSON.parseObject(result, Map.class);
			openId = StringUtil.getString(map1.get("openid"));
		}

		return openId;
	}
}
